iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
2
Modern Web

美麗的邂逅-與安室....伊春系列 第 11

幕後功臣 (service)

  • 分享至 

  • xImage
  •  
正如人子來,不是要受人的服事,乃是要服事人,並且要捨命,作多人的贖價。
  • ng generate component <component-name>
  • ng generate service <service-name>
  • ng generate class <model-name>
  • *ngFor="let book of books"

在這一回,我們談一下幕後英雄,前面我們介紹過的元件(component) 是有頭有臉的人物,有內涵(ts), 有容貌,是個枱面上的人物,包含許多顯示(html, css) 的設定;現在我們介紹服務(service)則沒有臉可露,就當個幕後工作人員,但也算是個角色,通常用以讀取後端程式的資料(以後會介紹)或是存放一些大家共用的資料(如本例)。
首先,產生一個服務, 可以用 CLI/VS.Code 或是 stackblitz, 在這個服務中,我們以存放聖經,舊約中37卷書的中英文名稱及縮寫為例,取名 abbreviations, 用CLI指令是

ng generate service abbreviations

注意產生的位置,各種方法會略有不同,stackblitz 會放在 /src, 而CLI 會放在 /src/service,這和將來(import)時使用的相對路徑會有些影響。在寫程式時,scope 很重要, 以這個服務而言,scope 有二層意義,第一層是何時產生,何時消滅,第二層是誰可以使用。有一個專有名詞,叫做注入(injection), 把服務想成是自來水,但注入點就像是水龍頭,水龍頭所在的位置會影響到 scope (何處有水?哪裡可以取水?誰可以用水?).
要設定注入點有二個方式,一個是註記在使用方,一個是註記在被使用方(服務本身)。不論是註記在哪一方,都是在建構函式(constructor)時注入(實化,產生物件)。若是註記在使用方,則寫在app.module.ts 中 @NgModule / provider 內,稱作 module injection, 例如:

@NgModule({
    providers: [ AbbreviationsService ]
})
export class AppModule { 
	constructor(private service: AbbreviationsService) { … }
 }

服務將在應用起啟時創建,同時整個應用的所有模組均可以使用,只有一份實體。稱作 module injection。若只在某元件內使用時,稱作 component injection, 可以在元件的.ts 檔中 @Component / provider 內註記,如:

@Component({
    providers: [AbbreviationsService]
})
export class MyComponent {
    constructor(private service: AbbreviationsService) {
        // ...
    }
}

另一種方式是註記在服務內(被使用方),先看一下我們用的服務內容:

abbreviations.service.ts
@Injectable({
  providedIn: 'root',
})

這和module injection 相當 (app.module.ts) 就不用改了。Root 也可以改成模組名稱,此時必須import 這個模組。
在寫服務之前,有個相關文內,因為我們要透過服務存取共用的資料,所以還要創建資料結構。因為在本例中,共有四個欄位,英文縮寫,英文書名,中文縮寫,中文書名。資料結構,即是所謂 model, 假設命名為 book, 用的是一般的class, 可以用

ng generate class book

其內容(book.ts)程式如下:

export class Book {
  constructor(
    private abbr_en: string,
    private en: string,
    private abbr_tw: string,
    private tw: string
  ){};
}

看不太懂?這是新版本提供物件宣告的寫法,這就相當於

export class Book {
    private abbr_en: string,
    private en: string,
    private abbr_tw: string,
    private tw: string
    constructor(a: string, b: string, c: string, d: string){
	  this.abbr_en=a;
       this.en=b;
       this.abbr_tw=c;
       this.tw=d;
};
}

是不是簡單多了,如果這個也看不懂,就要留言了。原本在這系統的文章發文前,目標是給完全初學者的,寫著寫著,其實還是有一些基本要求,例如,要有OOP,Java的基礎的。
如果程式比較複雜,可以把所有的資料結構放在同一個檔案夾內,如 /src/app/model, 服務也可以,例如 /src/app/service (就是注意 import 時的相對路徑即可), abbreviations.service.ts 如下:

import { Injectable } from '@angular/core';
import { Book } from './book';

@Injectable({
  providedIn: 'root',
})
export class AbbreviationsService {
  private BOOKS: Book[] = [
      new Book("Gen", "Genesys", "創", "創世紀"),
      new Book("Exo", "Exodus",     "出", "出埈及記"),
      … (省略, 若要全文, 請參考附錄連結) …
      ]   
  constructor() { }
  public books(): Book[] { return this.BOOKS; }
}

通常我們用全部大寫來表示常數,第一個字母大寫表示資料結構,第一個字母小寫表示變數。
範例中,在app.component中使用這個服務,因此只要改 app.component.ts

import { Component, OnInit} from '@angular/core';
import { AbbreviationsService } from '../abbreviations.service';
import { Book } from '../book';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  name = 'Faust-Service';
  books: Book[];
  constructor(private service: AbbreviationsService) { }
  ngOnInit() {
    this.books = this.service.books();
  }
}

可以留意AbbreviationsService是在constructor創建,在ngOnInit()中使用,設定成變數 this.books, 這個變數會在 app.component.html 中使用。即 {{ name }}

<div class="card my-5">
  <div class="card-body">
    <table class="table table-bordered table-striped">
      <tr>
        <th colspan="4">This is {{ name }} speaking!</th>
      </tr>
      <tbody>
        <tr *ngFor="let book of books">
          <td class="book_abbr">{{ book.abbr_en }}</td>
          <td class="book_name">{{ book.en }}</td>
          <td class="book_abbr">{{ book.abbr_tw }}</td>
          <td class="book_name">{{ book.tw }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

在這裡,我們看見Angular產生迴圈的作法 *ngFor="let book of books" 這相當於 Java 中的指令 for( book : books ), 而這個 books 即為 app.component.ts, export AppComponent, ngInit() 中的 this.books, 最後在 stackblitz 中顯示的結果如下:
https://ithelp.ithome.com.tw/upload/images/20190926/20120951fAOnsWhqQk.png

Reference

Link to project in stackblitz: demo
Link to project in stackblitz: source


上一篇
江湖走老,膽子變小 (Angular / unit test)
下一篇
大師引導 (bootstrap)
系列文
美麗的邂逅-與安室....伊春30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言